package com.xnyzc.lhy.didi.service.impl.xiecheng;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xnyzc.lhy.common.component.redis.RedisCommon;
import com.xnyzc.lhy.common.constant.RedisPrefixConstant;
import com.xnyzc.lhy.common.entity.GouldResult;
import com.xnyzc.lhy.common.entity.Qw;
import com.xnyzc.lhy.common.exception.DiDiException;
import com.xnyzc.lhy.common.penum.EDiDiErrorCode;
import com.xnyzc.lhy.common.penum.billRules.EUseTimeType;
import com.xnyzc.lhy.common.penum.cms.EVehicleType;
import com.xnyzc.lhy.common.penum.gould.EGould;
import com.xnyzc.lhy.common.util.CheckUtil;
import com.xnyzc.lhy.common.util.DateUtil;
import com.xnyzc.lhy.common.util.IDUtil;
import com.xnyzc.lhy.didi.entity.price.OaSysBillRules;
import com.xnyzc.lhy.didi.entity.price.OaSysBillSpecialRules;
import com.xnyzc.lhy.didi.entity.sys.OaSysCalendar;
import com.xnyzc.lhy.didi.mapper.area.DidiSysAreaMapper;
import com.xnyzc.lhy.didi.mapper.area.OaSysAreaMapper;
import com.xnyzc.lhy.didi.mapper.dict.OaSysDictMapper;
import com.xnyzc.lhy.didi.mapper.price.OaSysBillRulesMapper;
import com.xnyzc.lhy.didi.mapper.price.OaSysBillSpecialRulesMapper;
import com.xnyzc.lhy.didi.mapper.sys.OaSysCalendarMapper;
import com.xnyzc.lhy.didi.service.xiecheng.PriceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * <p>
 * 价格相关
 * </p>
 *
 * @author zhengxu
 * @since 2019-09-25
 */
@Service
public class PriceServiceImpl implements PriceService {

    @Autowired
    private DidiSysAreaMapper didiSysAreaMapper;

    @Autowired
    private OaSysAreaMapper oaSysAreaMapper;

    @Autowired
    private OaSysBillRulesMapper oaSysBillRulesMapper;

    @Autowired
    private OaSysDictMapper oaSysDictMapper;

    @Autowired
    private RedisCommon redisCommon;

    @Autowired
    private OaSysBillSpecialRulesMapper oaSysBillSpecialRulesMapper;

    @Autowired
    private OaSysCalendarMapper calendarMapper;

    @Override
    public HashMap<String, Object> getPriceDetail(Integer channel, String code, EVehicleType vehicleType, Integer callType, Date startDate, GouldResult.RouteBean router) {
        List<HashMap<String, Object>> list = new ArrayList<>();
        int cnt = 0;
        // 查询价格模板
        OaSysBillRules oaSysBillRules = selectBillRules(channel, code, vehicleType.getValue(), callType, startDate);
        if (CheckUtil.objIsEmpty(oaSysBillRules)) {
            throw DiDiException.create(EDiDiErrorCode.noHaveSpecialSheet);
        }
        // 查询特殊价格模板
        List<OaSysBillSpecialRules> oaSysBillSpecialRules = selectBillSpecialRules(oaSysBillRules.getRulesId());

        // 获取所有的路径规划
        List<GouldResult.RouteBean.PathsBean> paths = router.getPaths();
        int size = paths.size();
        for (int i = 0; i < size; i++) {
            GouldResult.RouteBean.PathsBean path = paths.get(i);
            // 获取里程
            if (CheckUtil.strIsEmpty(path.getDistance())) {
                throw DiDiException.create(EDiDiErrorCode.mileageObtainFail);
            }
            BigDecimal distanceM = new BigDecimal(path.getDistance());
            BigDecimal distanceKm = distanceM.divide(new BigDecimal(1000));
            distanceKm = distanceKm.setScale(1, BigDecimal.ROUND_CEILING);
            // 获取行驶时间
            if (CheckUtil.strIsEmpty(path.getDuration())) {
                throw DiDiException.create(EDiDiErrorCode.travelTimeObtainFail);
            }
            Double time = Math.rint(Double.valueOf(path.getDuration()) / 60);
            String mtolls = path.getTolls();
            int tolls = 0;
            if (CheckUtil.strIsNotEmpty(mtolls)) {
                tolls = Integer.valueOf(mtolls);
            }
            HashMap<String, Object> priceMapNew = this.ExpectedFee(code, vehicleType, time, distanceKm.doubleValue(), tolls, startDate, oaSysBillRules, oaSysBillSpecialRules);
            priceMapNew.put("distanceKm", distanceKm);
            priceMapNew.put("distanceM", distanceM);
            priceMapNew.put("duration", path.getDuration());
            list.add(priceMapNew);
            if (list.size() > 1) {
                HashMap<String, Object> priceMapOld = list.get(cnt);
                BigDecimal totalFeeOld = (BigDecimal) priceMapOld.get("totalFee");
                BigDecimal totalFeeNew = (BigDecimal) priceMapNew.get("totalFee");
                // 获取比较价格最低
                if (totalFeeNew.compareTo(totalFeeOld) < 0) {
                    cnt = i;
                }
            }
        }
        HashMap<String, Object> priceMap = list.get(cnt);
        // 获取选择的规划路径
        GouldResult.RouteBean.PathsBean pathsBean = paths.get(cnt);
        // 保存预估点位
        List<GouldResult.RouteBean.PathsBean.StepsBean> steps = pathsBean.getSteps();
        StringBuffer stringBuffer = new StringBuffer();
        for (GouldResult.RouteBean.PathsBean.StepsBean stepsBean : steps) {
            stringBuffer.append(stepsBean.getPolyline());
            stringBuffer.append(";");
        }
        stringBuffer.deleteCharAt(stringBuffer.length() - 1);
        priceMap.put(EGould.gouldPolyline.getValue(), stringBuffer.toString());
        priceMap.put("robable", oaSysBillRules.getRobable());
        return priceMap;
    }

    private OaSysBillRules selectBillRules(Integer channel, String cityCode, Integer vehType, Integer callType, Date date) {
        //判断是否为休息日
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        OaSysCalendar oaSysCalendar = calendarMapper.selectOne(Qw.create().eq(OaSysCalendar.DATE, sdf.format(date)));
        EUseTimeType timeType = EUseTimeType.conventional;
        //获取今天是否是节假日
        if (CheckUtil.objIsNotEmpty(oaSysCalendar)) {
            if (!oaSysCalendar.getWorkingDay().equals(0)) {
                timeType = EUseTimeType.offDay;
            }
        }
//        Long compayId = 962641195630592L;
        //查询redis是否存在计价规则
        OaSysBillRules oaSysBillRules = null;
        String key = RedisPrefixConstant.BILL_RULES + "_" + channel + "_" + cityCode + "_" + timeType.getType() + "_" + vehType + "_" + callType;
        String oaSysBillRulesJson = redisCommon.getStr(key);
        if (CheckUtil.strIsNotEmpty(oaSysBillRulesJson)) {
            oaSysBillRules = JSON.parseObject(oaSysBillRulesJson, OaSysBillRules.class);
        }
        //如果计价规则为空,继续向数据库寻找
        if (CheckUtil.objIsEmpty(oaSysBillRules)) {
            QueryWrapper<OaSysBillRules> areaQueryWrapper = new QueryWrapper<>();
            areaQueryWrapper.eq(OaSysBillRules.CHANNEL_ID, channel);
            areaQueryWrapper.eq(OaSysBillRules.CITY_ID, cityCode);
            // 目前先规定一个公司ID
//            areaQueryWrapper.eq(OaSysBillRules.COMPANY_ID, compayId);
            areaQueryWrapper.eq(OaSysBillRules.USE_TIME_TYPE, timeType.getType());
            areaQueryWrapper.eq(OaSysBillRules.VEHICLE_TYPE, vehType);
            areaQueryWrapper.eq(OaSysBillRules.CALL_TYPE, callType);
            areaQueryWrapper.eq(OaSysBillRules.IS_DISABLE, 0);
            // 查询普通时间
            List<OaSysBillRules> billList = oaSysBillRulesMapper.selectList(areaQueryWrapper);
            if (billList.size() > 0) {
                OaSysBillRules billRule = billList.get(0);
                if (CheckUtil.objIsNotEmpty(billRule)) {
                    oaSysBillRules = billRule;
                    redisCommon.set(key, JSON.toJSONString(oaSysBillRules));
                }
            }
        }
        return oaSysBillRules;
    }

    public List<OaSysBillSpecialRules> selectBillSpecialRules(Long rulesId) {
        List<OaSysBillSpecialRules> rules = new ArrayList<>();
        //从redis中获取特殊时段计费规则
        String key = RedisPrefixConstant.BILL_SPECIAL_RULES + rulesId;
        String rulesJson = redisCommon.getStr(key);
        if (CheckUtil.strIsNotEmpty(rulesJson)) {
            rules = JSON.parseArray(rulesJson, OaSysBillSpecialRules.class);
        }
        //如果为空继续向数据库获取
        if (null == rules || rules.size() <= 0) {
            rules = oaSysBillSpecialRulesMapper.selectList(Qw.create().eq(OaSysBillSpecialRules.RULES_ID, rulesId));
            redisCommon.set(key, JSON.toJSONString(rules));
        }
        return rules;
    }

    /**
     * 预估价金额
     * 做不到十分准确,只能说预估大约
     *
     * @return
     */
    public HashMap<String, Object> ExpectedFee(String cityCode, EVehicleType vehicleType, double time, double mileage, int tolls, Date startDate, OaSysBillRules oaSysBillRules, List<OaSysBillSpecialRules> oaSysBillSpecialRules) {
        //行驶时长
        BigDecimal timeBig = BigDecimal.valueOf(time);
        //需要计算费用的里程
        BigDecimal mileageBig = BigDecimal.valueOf(mileage);
        //总里程
        BigDecimal totalMileag = BigDecimal.valueOf(mileage);
        //保证最后数值正确,先获取最后两位小数后计算
        mileageBig = mileageBig.setScale(2, BigDecimal.ROUND_HALF_DOWN);
        timeBig = timeBig.setScale(2, BigDecimal.ROUND_HALF_DOWN);
        //如果总时长为0,默认平均里程为总里程
        BigDecimal divide = new BigDecimal(0);
        //默认行程结束时间
        Date endDate = new Date(((long) time * 60 * 1000) + startDate.getTime());
        HashMap<String, Object> result = new HashMap<>();
        result.put("mileageCount", mileage);
        result.put("timeCount", time);
        result.put("type", vehicleType.getValue());
        result.put("estimateId", IDUtil.nextId());
        result.put("lable", vehicleType.getDesc());
        result.put("specialTimeFee", new BigDecimal(0.00));
        result.put("specialDistanceFee", new BigDecimal(0.00));
        result.put("specialDistance", new BigDecimal(0));
        result.put("specialTime", new BigDecimal(0));
        result.put("rulesId", 0L);
        result.put("emptyFee", new BigDecimal(0.00));
        result.put("emptyDistance", new BigDecimal(0));
        result.put("tolls", new BigDecimal(tolls));
        // 判断是否为节假日，获取计价规则
        if (CheckUtil.objIsNotEmpty(oaSysBillRules)) {
            result.put("rulesId", new BigDecimal(oaSysBillRules.getRulesId()));
            //设置起步时长,与起步里程
            BigDecimal startTime = new BigDecimal(oaSysBillRules.getStartTime());
            BigDecimal startMileage = new BigDecimal(oaSysBillRules.getStartMileage());
            result.put("startTime", startTime);
            result.put("startMileage", startMileage);
            //将起步时长换算为毫秒
            Long startTimeMs = startTime.longValue() * 60 * 1000;
            startDate = new Date(startTimeMs + startDate.getTime());
            //总里程去除起步价时长,起步里程
            mileageBig = mileageBig.subtract(startMileage).setScale(2, BigDecimal.ROUND_HALF_DOWN);
            timeBig = timeBig.subtract(startTime).setScale(2, BigDecimal.ROUND_HALF_DOWN);
            //获取减去起步价时长,起步价里程
            if (mileageBig.doubleValue() > 0) {
                //如果总时长为0,默认平均里程为总里程
                if (timeBig.doubleValue() != 0) {
                    divide = mileageBig.divide(timeBig, 2, BigDecimal.ROUND_HALF_DOWN);
                }
            }
            SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm");
            String timeBegin = dateFormat.format(startDate);
            String timeEnd = dateFormat.format(endDate);
            //获取全部特殊时间段
            if (oaSysBillSpecialRules.size() > 0) {
                for (OaSysBillSpecialRules oaSysBillSpecialRule : oaSysBillSpecialRules) {
                    //当前当前时间是否在此时间段内
                    BigDecimal startDateIsRange = DateUtil.isInDate(oaSysBillSpecialRule.getStartTime(), oaSysBillSpecialRule.getEndTime(), timeBegin, timeEnd);
                    if (startDateIsRange.doubleValue() <= 0.0) {
                        continue;
                    }
                    //判断是否订单开始时间,是否在特殊时段内-起步费
                    if (DateUtil.isRange(oaSysBillSpecialRule.getStartTime(), oaSysBillSpecialRule.getEndTime(), startDate)) {
                        BigDecimal startFee = oaSysBillSpecialRule.getStartFee();
                        result.put("startFee", startFee);
                    }

                    //获取在此时间段的分钟
                    BigDecimal rangeVersionTwo = DateUtil.isInDate(oaSysBillSpecialRule.getStartTime(), oaSysBillSpecialRule.getEndTime(), timeBegin, timeEnd);
                    if (timeBig.doubleValue() <= rangeVersionTwo.doubleValue()) {
                        rangeVersionTwo = timeBig;
                    }
                    if (timeBig.doubleValue() > 0.00 && rangeVersionTwo.doubleValue() > 0.00) {
                        //获取时长费
                        BigDecimal specialTimeFee = rangeVersionTwo.multiply(oaSysBillSpecialRule.getSpecialTimeFee());
                        BigDecimal specialTimeFee1 = ((BigDecimal) result.get("specialTimeFee")).add(specialTimeFee).setScale(2, BigDecimal.ROUND_HALF_DOWN);
                        result.put("specialTimeFee", specialTimeFee1);
                        //减去计算过的时长
                        timeBig = timeBig.subtract(rangeVersionTwo);
                        //计价时间转换为秒
                        BigDecimal specialTime1 = (BigDecimal) result.get("specialTime");
                        result.put("specialTime", specialTime1.add(DateUtil.minutesConversionSeconds(rangeVersionTwo)));
                    }
                    //如果里程大于0
                    if (mileageBig.doubleValue() > 0.00 && rangeVersionTwo.doubleValue() > 0.00) {
                        //每分钟里程数
                        BigDecimal specialDistance = rangeVersionTwo.multiply(divide);
                        //里程费
                        BigDecimal specialDistanceFee = specialDistance.multiply(oaSysBillSpecialRule.getSpecialMileageFee());
                        BigDecimal specialDistanceFee1 = ((BigDecimal) result.get("specialDistanceFee")).add(specialDistanceFee).setScale(2, BigDecimal.ROUND_HALF_DOWN);
                        result.put("specialDistanceFee", specialDistanceFee1);
                        //减去计算过的里程
                        mileageBig = mileageBig.subtract(specialDistance);
                        //计价里程转换为米
                        BigDecimal specialDistance1 = (BigDecimal) result.get("specialDistance");
                        result.put("specialDistance", specialDistance1.add(specialDistance.multiply(new BigDecimal(1000))));
                    }
                }
            }
            //非特殊时间段
            result = ordinaryTime(oaSysBillRules, timeBig, mileageBig, result, totalMileag);
        }
        BigDecimal totalFee = new BigDecimal(tolls);
        BigDecimal specialTimeFee = (BigDecimal) result.get("specialTimeFee");
        if (CheckUtil.objIsNotEmpty(specialTimeFee)) {
            totalFee = totalFee.add(specialTimeFee);
        }
        BigDecimal specialDistanceFee = (BigDecimal) result.get("specialDistanceFee");
        if (CheckUtil.objIsNotEmpty(specialDistanceFee)) {
            totalFee = totalFee.add(specialDistanceFee);
        }
        BigDecimal startFee = (BigDecimal) result.get("startFee");
        if (CheckUtil.objIsNotEmpty(startFee)) {
            totalFee = totalFee.add(startFee);
        }
        BigDecimal emptyFee = (BigDecimal) result.get("emptyFee");
        if (CheckUtil.objIsNotEmpty(emptyFee)) {
            totalFee = totalFee.add(emptyFee);
        }
        Integer premium = oaSysBillRules.getPremium();
        if (CheckUtil.objIsNotEmpty(premium)) {
            BigDecimal mpremium = new BigDecimal(premium);
            result.put("premium", mpremium);
            totalFee = totalFee.add(mpremium);
        }
        result.put("totalFee", totalFee);
        BigDecimal drawRatio = oaSysBillRules.getDrawRatio();
        result.put("drawFee", totalFee.subtract(totalFee.multiply(drawRatio)));
        return result;
    }

    /**
     * 普通时间段计费
     *
     * @param billRules
     * @return
     */
    public HashMap<String, Object> ordinaryTime(OaSysBillRules billRules, BigDecimal timeBig, BigDecimal mileageBig, HashMap<String, Object> result, BigDecimal totalMileag) {
        BigDecimal startFee = billRules.getStartFee();
        if (!result.containsKey("startFee")) {
            result.put("startFee", startFee);
        }
        //获取多余里程费用
        BigDecimal computingMileageFee = new BigDecimal(0);
        if (mileageBig.doubleValue() > 0.0) {
            computingMileageFee = mileageBig.multiply(billRules.getMileageFee());
            BigDecimal specialDistanceFee = (BigDecimal) result.get("specialDistanceFee");
            result.put("specialDistanceFee", specialDistanceFee.add(computingMileageFee));
        } else {
            //防止出现负数
            mileageBig = new BigDecimal(0);
        }
        //获取多余时长费
        BigDecimal specialTimeFee = new BigDecimal(0);
        if (timeBig.doubleValue() > 0.0) {
            specialTimeFee = timeBig.multiply(billRules.getTimeFee());
            BigDecimal specialTimeFees = (BigDecimal) result.get("specialTimeFee");
            result.put("specialTimeFee", specialTimeFees.add(specialTimeFee));
        } else {
            //防止出现负数
            timeBig = new BigDecimal(0);
        }
        //获取远途里程计算费用
        BigDecimal distanceKm = new BigDecimal(billRules.getDistanceKm());
        BigDecimal distanceKmFee = new BigDecimal(0);
        if (totalMileag.doubleValue() > distanceKm.doubleValue()) {
            //获取远途里程
            BigDecimal distanceMileage = totalMileag.subtract(distanceKm);
            distanceKmFee = distanceMileage.multiply(billRules.getDistanceFee());
            result.put("emptyFee", distanceKmFee);
            result.put("emptyDistance", distanceMileage.multiply(new BigDecimal(1000)));
        }
        //计价里程转换为米
        BigDecimal specialDistance = (BigDecimal) result.get("specialDistance");
        result.put("specialDistance", specialDistance.add(mileageBig.multiply(new BigDecimal(1000))));
        //计价时间转换为秒
        BigDecimal specialTime1 = (BigDecimal) result.get("specialTime");
        result.put("specialTime", specialTime1.add(DateUtil.minutesConversionSeconds(timeBig)));
        return result;
    }
}
